/**
 * Copyright (C), 2017-2018, yinxue-soft
 * FileName: RpcInjectPostProcessor
 * Author:   zengjian
 * Date:     2018/10/8 16:14
 * Description: rpc代理类注入
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package org.yinxue.framework.rpc.spring;



import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.yinxue.framework.rpc.utils.ProxyUtil;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.yinxue.framework.rpc.RpcRequest;
import org.yinxue.framework.rpc.RpcResponse;
import org.yinxue.framework.rpc.annotation.RpcConsumer;
import org.yinxue.framework.rpc.annotation.RpcProvider;
import org.yinxue.framework.rpc.register.LocalRegister;
import org.yinxue.framework.rpc.register.ServiceExporter;
import org.yinxue.framework.rpc.tansport.NettyClient;
import org.yinxue.framework.rpc.tansport.NettyServer;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 〈rpc客户端代理类注入〉<br>
 * 〈一句话描述〉
 *
 * @author zengjian
 * @create 2018/10/8 16:14
 */
public class RpcInjectPostProcessor implements BeanPostProcessor {

    private LocalRegister localRegister = new LocalRegister();
    private NettyClient nettyClient = new NettyClient();
    private NettyServer nettyServer;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class clz = bean.getClass();
        Field[] fields = clz.getDeclaredFields();
        for (final Field field : fields) {
            if (field.isAnnotationPresent(RpcConsumer.class)) {
                try {
                    injectProxy(bean, field);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        // 如果服务提供者
        if (clz.isAnnotationPresent(RpcProvider.class)) {
            register(bean);
            // 异步发布服务
            try {
                nettyServer = new NettyServer(this.localRegister);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread() {
                @Override
                public void run() {
                    nettyServer.start();
                }
            }.start();
        }
        return bean;
    }

    private void injectProxy(Object bean, final Field field) throws Exception {
        field.setAccessible(true);
        RpcConsumer rpcConsumer = field.getAnnotation(RpcConsumer.class);
        field.set(bean, ProxyUtil.doCreateProxyByCGLIB(field.getType(), new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                RpcRequest rpcRequest = buildRpcRequest(method, args);
                RpcResponse response = nettyClient.sendRpcRequest(rpcRequest);
                return response.getValue();
            }

            private RpcRequest buildRpcRequest(Method method, Object[] args) {
                RpcRequest rpcRequest = new RpcRequest();
                rpcRequest.setServiceName(field.getType().getSimpleName() + "_" + method.getName() + "_" + args.length + "_" + 0);
                rpcRequest.setArgs(args);
                return rpcRequest;
            }
        }));
    }

    private void register(Object bean) {
        Method[] methods = bean.getClass().getDeclaredMethods();
        for (Method method : methods) {
            processRegister(method, bean);
        }
    }

    private void processRegister(Method method, Object bean) {
        ServiceExporter exporter = new ServiceExporter();
        int version = 0;
        String name = "EchoService"/*bean.getClass().getSuperclass().getSimpleName()*/ + "_" + method.getName() + "_" + method.getParameterTypes().length + "_" + version;
        exporter.setServiceName(name);
        exporter.setInstance(bean);
        exporter.setVersion(version);
        exporter.setMethod(method);
        localRegister.register(exporter);
    }
}